import data.Value;
import functionnodes.FunctionNode;
import functionnodes.FunctionNodeAdd;
import functionnodes.FunctionNodePrint;
import functionnodes.FunctionNodeIf;
import functionnodes.FunctionNodeLt;
import functionnodes.ListOfNodes;
//import functionnodes.FunctionNode;

 
 
 
 
 
 
  
 
 
/**
 * @author pbalogh
 * OK -- big things first.
 * 
 * What's the core of this thing?
 * 
 * Reducing XML nodes to Value objects.
 * 
 * FunctionNodes reduce arrays of Value objects to a single Value object.
 * 
 * reduceValues()
 * 
 * Macros reduce an array of XML nodes to a *different* XML node, 
 * which the evaluator then converts to a single Value object.
 * 
 *  reduceXMLtoXML()
 * 
 * Special functions like "if" receive a mix of Values and XML nodes
 * and, based upon the Values, decide which XML nodes to evaluate
 * and sometimes they return a Value as well
 * 
 * 
 * A Value object has a raw() method that returns:
 * 
 * a string,
 * 
 * a number, 
 * 
 * or whatever else the raw value is.
 * 
 * I think I need to create a new syntax.
 * setc means "set in this clip"
 * and the first arg has to be a reference to a movie clip
 * the symbol __root must = _root
 * 
 * I need to do all this shit before I can really get down
 * to the business of figuring out how to attach movie clips
 * or create empty ones and draw into them
 * 
 * what about calling a method on a movie clip?
 * how do I do that?
 * or setting a property?
 * 
 * maybe create a new functionnode, withclip,
 * and the first argument is a reference to a valuenode wrapped around the clip.
 * 
 * the second argument must be a symbol ---
 * and it resolves to a value node of some kind.
 * 
 * here's where it gets complex.
 * if I say myclip._x, I could either be retrieving a value or setting it.
 * context means everything.
 * 
 * I would need to have the context explicitly differentiate.
 * hence the need for a verb like setc or getc.
 * 
 * and for that matter setf and getf, which are like the usual setf and getf.
 * 
 * arg, this is all too fucking complicated.
 * because we need to have scope, right?
 * lexical scope?
 * so we can't just shunt away our scope object just like that.
 * 
 * so what if setc and getc happened in our usual lexical scope
 * and just happened to receive a clip's symbolic reference
 * as their first arg?
 * 
 * much cleaner than the whole "with" thing.
 * 
 * so, for example:
 * 
<list>
	 <symbol>setf</symbol>
	 <q>myclip</q>
	 <symbol>__root</symbol>
</list>
<list>
  	<symbol>setc</print>
  	<symbol>myclip</symbol>
  	<q>_x</q>
  	<symbol>100</symbol>
</list>
<list>
  	<symbol>print</print>
  	<list>
  		<symbol>getc</symbol>
  		<symbol>myclip</symbol>
  		<q>_x</q>
  	</list>
</list>
 * 
 * the good thing about that approach
 * is that it makes explicit
 * the whole act of looking for a movie clip
 * 
 * Am I comfortable with the idea
 * that getf returns a Value object?
 * 
 * Am I really?
 * 
 * 
 * OK, big note -- setInScope is really a factor of setf.
 * And since I'm overriding the built-in attachMovie,
 * I'll have to put in hooks inside each clip I attach
 * (or create!)
 * for onEnterFrameLisp, onPressLisp, onReleaseLisp, etc.
 * i.e.,
 * clip.onEnterFrame = function() { Evaluator.evaluate( this.onEnterFrameLisp, this ); };
 * with the caveat that the clip's scopeparent is set to ... its lexical scope at time of creation?
 * 
 * 
 * TODO: Create a withc functionnode, so that we can 
 * 
 * TODO: Learn about typed arrays in MTASC...
 */
class Evaluator {
	
	public static var evaluator:Evaluator = null;
//	static var f:functionnodes.FunctionNode;

	public function Evaluator( )
	{
		trace("Constructing Evaluator");

		trace("functionnodes.FunctionNode = " + functionnodes.FunctionNode);
		trace(eval("functionnodes." + "FunctionNode"));
	}
	
	public static function init( topscope:Object )
	{
		/*
		var tempXML:XML = new XML( "<symbol>add</symbol>" );
		topscope[ "symboladd" ] = new Value( Value.FUNCTIONNODE, new FunctionNodeAdd(), 
		XMLNode( tempXML.firstChild ));
		tempXML = new XML( "<symbol>print</symbol>" );
		topscope[ "symbolprint" ] = new Value( Value.FUNCTIONNODE, new FunctionNodePrint(), 
		XMLNode( tempXML.firstChild ));
		tempXML = new XML( "<symbol>if</symbol>" );
		topscope[ "symbolif" ] = new Value( Value.FUNCTIONNODE, new FunctionNodeIf(), 
		XMLNode( tempXML.firstChild ));
		tempXML = new XML( "<symbol>lt</symbol>" );
		topscope[ "symbollt" ] = new Value( Value.FUNCTIONNODE, new FunctionNodeLt(), 
		XMLNode( tempXML.firstChild ));
		*/
		var list:Array = ListOfNodes.LIST;
		for( var i:Number = 0; i < list.length; i++)
		{
			var constructor:Function = eval("functionnodes." + list[i] );
			trace("constructor = " + constructor );
			var fn:FunctionNode = new constructor();
			trace("fn.funcname = " + fn.funcname );
			var tempXML:XML = new XML( "<symbol>" + fn.funcname + "</symbol>" );
			topscope[ "symbol" + fn.funcname ] = new Value( Value.FUNCTIONNODE, fn, 
			XMLNode( tempXML.firstChild ));
		}
		topscope["symbol__root"] = new Value( Value.MOVIECLIP, _root );
	}
	
	public static function convertXMLtoValue(x:XMLNode, scope:Object) : Value
	{
	
		trace("in convertXMLtoValue, x.nodeName is "+x.nodeName);
		if (x.nodeName == "flisp") {
			var returnvalue:Value;
			for( var i:Number = 0; i < x.childNodes.length; i++)
			{
				returnvalue = convertXMLtoValue( x.childNodes[ i ], scope );
			}
			return returnvalue;
		}
		else if (x.nodeName == "symbol") 
		{
			//trace("SYMBOL!");
			if (!isNaN(Number(x.firstChild.nodeValue))) {
				var val:Number = Number(x.firstChild.nodeValue);
				return new Value( Value.NUMBER, val );
			} 
			else 
			{
				trace("IN SYMBOL: going to getValueInScope for " + x);
				if (x.firstChild.childNodes.length>0) {
					//			//trace("IN SYMBOL: x.firstChild = " + x.firstChild );
					// in symbol, we have other nodes nested--
					// i.e., the name of the symbol isn't being given to us as a string
					// but rather as an expression that will *yield* the string
					var returnvalue:Value = lispEval(x.firstChild, scope);
					return getValueInScope(returnvalue.raw(), scope);
				} else {
					trace("without calling lispEval on it");
					return getValueInScope(x.firstChild.nodeValue, scope);
				}
			}
		} else if (x.nodeName == "q") {
			trace("OUR XML IS " + x);
			// is this a string literal, or is the <q> wrapped around something else?
			trace("x.childNodes.length = " + x.childNodes.length ); 
			trace("x.childNodes[0] = " + x.childNodes[0] );
			trace("x.firstChild = " + x.firstChild );
			trace("x.firstChild.nodeValue = " + x.firstChild.nodeValue ); 
			trace("x.firstChild.childNodes.length = " + x.firstChild.childNodes.length ); 
			if( x.firstChild.childNodes.length == 0 ) // no nested nodes here -- a string literal
			{
				return new Value( Value.STRING, x.firstChild.nodeValue );
			}
			else
			{
				trace("Returning " + x.firstChild );
				return new Value( Value.XMLNODE, x.firstChild );
			}
			
		} else if (x.nodeName == "list") {
			//trace("LIST!");
			return lispEval(x, scope);
		} else {
			//trace("OPERATOR!");
			var id:String = x.nodeName;
			var returnval:Value = getValueInScope(id, scope);
			if (returnval == undefined) {
				//trace("IN GETVALUE, ERROR RESOLVING FUNCTION "+id+" in scope "+scope);
			}
			return returnval;
		}
	}
	public static function lispEval(x:XMLNode, scope:Object) : Value
	{
		return lispApply(car(x), cdr(x), scope);
	}
	public static function car(x:XMLNode):XMLNode {
		return x.childNodes[0];
	}
	public static function cdr(x:XMLNode):Array {
		var resultarray:Array = x.childNodes.slice(1);
		return resultarray;
	}
	public static function lispApply(funcNode:XMLNode, xmlDataNodes:Array, scope:Object ) : Value
	{
		var func:FunctionNode = convertXMLtoValue(funcNode, scope).raw();
		
		if( func.type == FunctionNode.FUNCTION )
		{
			var args:Array = new Array();
			for (var i:Number = 0; i<xmlDataNodes.length; i++) {
				args.push(convertXMLtoValue(xmlDataNodes[i], scope));
			}
			return func.reduceValues(scope, args);
		}
		else if (func.type == FunctionNode.SPECIALFUNCTION )
		{
			return func.reduceXMLtoValue( scope, xmlDataNodes );
		}
		else if (func.type == FunctionNode.MACRO )
		{
			return convertXMLtoValue( func.reduceXMLtoXML( scope, xmlDataNodes ), scope );
		}
	}
	/*
	public static function getFunctionString(x:XMLNode, scope:Object):Object {
	//	//trace("getFunctionString: "+x);
		if (x.nodeName == "symbol") {
			return x.firstChild.nodeValue;
		} else if (x.nodeName == "list") {
	//		//trace("Your function is a list");
	//		//trace("And its first childnode is "+x.childNodes[0]);
	//		//trace("x.childNodes[0].firstChild.nodeValue = "+x.childNodes[0].firstChild.nodeValue);
			if (x.childNodes[0].firstChild.nodeValue == "new") {
				return "newconstructor";
			}
		}
	}
	public static function getString(x:XMLNode, scope:Object):Object {
		if (x.nodeName == "symbol") {
			//trace("SYMBOL!");
			if (!isNaN(Number(x.firstChild.nodeValue))) {
				return x.firstChild.nodeValue;
			} else {
				////trace("In getString, x.firstChild.childNodes.length = "+x.firstChild.childNodes.length);
				if (x.firstChild.childNodes.length>0) {
					var val:Object = lispEval(x.firstChild, scope);
					////trace("IN SYMBOL: val = "+val);
					return val;
				} else {
					return x.firstChild.nodeValue;
				}
			}
		} else if (x.nodeName == "list") {
			//I think we need to eval this list to get its value...
			lispEval(x, scope);
		} else {
			return x.firstChild.nodeValue;
		}
	}
	*/
	
	public static function getValueInScope(targ:String, scope:Object):Value {
		var val:Value;
		trace("getValueInScope trying to find " + targ + " in scope " + scope);
		if (scope[targ] == undefined) {
			// uh oh -- not in this scope....
			//well, it might be a symbol...
			//Here's our voodoo for trying to deal with the keyword "this"
			trace("but it's undefined per se...");
			if (targ == "this") {
				val = new Value( Value.MOVIECLIP, getNextEnclosingClip( scope) );
				trace("so I'll send the clip, which is " + val );
			} else if (scope["symbol"+targ] != undefined) {
				val = scope["symbol"+targ];
			} else {
				//time to see if we can resolve this upward to an enclosing scope!
				if (scope["scopeparent"] != undefined) {
					val = getValueInScope(targ, scope["scopeparent"]);
				} else {
					//trace("ERROR! Can't get "+targ+" in scope "+scope+" and my scopeparent = "+scope["scopeparent"]);
				}
			}
		} else {
			val = scope[targ];
		}
		trace("found val " + val);
		trace("And is it a Value? " + (val instanceof Value) );
		trace("Its type is " + val.type);
		return val;
	}
	

	
	public static function getNextEnclosingClip( scope:Object ):MovieClip
	{
		if( scope instanceof MovieClip)
		{
			return MovieClip( scope );
		}
		else
		{
			return getNextEnclosingClip( scope["scopeparent"]);
		}
		
	}
	public static function setInScope( scope : Object, varname : Value, val : Value) : Value 
	{
		scope["symbol" + varname.raw()] = val;
		trace("In setInScope, just set symbol"+varname.raw()+" to " + val + " in scope " + scope );
		trace("And is " + val + " a Value? " + ( val instanceof Value ));
		return val;
	}

}
	
